#pragma once

#include <iostream>
#include <set>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
#include "LockGuard.hpp"

using task_t = std::function<void()>;

struct Cmp
{
    bool operator()(const InetAddr &a, const InetAddr &b)
    {
        return a.Port() > b.Port() || a.IP() > b.IP();
    }
};

class MessageRoute
{
private:
    bool IsExit(const InetAddr &user)
    {
        auto iter = _online_users.find(user);
        if(iter == _online_users.end())
            return false;
        return true;
    }

public:
    MessageRoute()
    {
        pthread_mutex_init(&_mutex, nullptr);
    }

    void AddUser(const InetAddr &user)
    {
        LockGruad lockgurad(&_mutex);

        if (!IsExit(user))
        {
            _online_users.insert(user);
        }
    }

    void DelUser(const InetAddr &user)
    {
        LockGruad lockgurad(&_mutex);
        if (IsExit(user))
        {
            auto iter = _online_users.begin();
            for (; iter != _online_users.end(); iter++)
            {
                if (*iter == user)
                {
                    _online_users.erase(iter);
                    break;
                }
            }
        }
    }

    void RouteHelper(int sfd, const std::string &message, const InetAddr &who)
    {
        LockGruad lockgurad(&_mutex);
        int n = 1;
        // 向所有用户发送消息
        for (auto &user : _online_users)
        {
            std::string sendMessage = "\n[" + who.IP() + " : " + std::to_string(who.Port()) + "] -> " + message + "\n";
            struct sockaddr_in ClientSockAddr = user.SockAddr();
            ssize_t n = sendto(sfd, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr *)&(ClientSockAddr), sizeof(struct sockaddr_in));

            if (n != sendMessage.size())
                LOG(DEBUG, "server sento");
        }
    }
    void Route(int sfd, const std::string &message, const InetAddr &who)
    {
        // 用户首次发送消息时，需要将其加入用户列表中
        AddUser(who);

        // 当用户输入Quit时, 退出chat，删除该用户
        if (message == "Quit")
        {
            DelUser(who);
            // return ; // 这里不直接退出，想让其他人直到该用户退出了
        }

        // 向线程池入任务
        // 线程池执行的任务函数，参数为空，返回值为空 --> 需要构建任务对象

        // 这里使用类内函数进行构建
        task_t t = std::bind(&MessageRoute::RouteHelper, this, sfd, message, who);

        // 消息转发任务使用线程池完成
        ThreadPool<task_t>::GetInstance()->Push(t);
    }

    ~MessageRoute()
    {
        pthread_mutex_destroy(&_mutex);
    }

private:
    std::set<InetAddr, Cmp> _online_users; // 每一个用户使用IP和端口号进行标识
    pthread_mutex_t _mutex;
};